package punctuatedEquilibrium;

public class Individual {
	private static double		SLOPE_OF_POSITIVE_EFFECTS	= 0.55;
	private static double		SLOPE_OF_NEGATIVE_EFFECTS	= 0.58;
	private static final double	MINIMUM_SURVIVABILITY		= 0;
	private static final double	MAXIMUM_SURVIVABILITY		= 1;
	private boolean				alive;
	private double				probabilityOfSurvival;
	private Phenotype			traits;
	private Phenotype			currentOptimalTraits;

	public Individual(Phenotype traits,
			IndividualParameters individualParameters) {
		alive = true;
		probabilityOfSurvival = 1;
		this.traits = traits;
		SLOPE_OF_POSITIVE_EFFECTS = individualParameters.positiveModifier();
		SLOPE_OF_NEGATIVE_EFFECTS = individualParameters.negativeModifier();
	}

	public void evaluateFitnessBasedOn(Phenotype optimalTraits,
			Phenotype lastGenerationsTraits) {
		currentOptimalTraits = optimalTraits;
		double adaption = evaluateSurvivalRateBasedOnDegreeOfAdaption();
		double negativeEffectOfMutation = applyNegativeEffectOfMutation(lastGenerationsTraits);
		probabilityOfSurvival = arithmeticMean(adaption,
				negativeEffectOfMutation);
	}

	private double arithmeticMean(double firstNumber, double secondNumber) {
		double sum = firstNumber + secondNumber;
		double mean = sum / 2;
		return mean;
	}

	private double applyNegativeEffectOfMutation(Phenotype lastGenerationsTraits) {
		Double[] arrayOfTraits = traits.getArrayOfVariables();
		Double[] arrayOfLastGenerationsTraits = lastGenerationsTraits
				.getArrayOfVariables();
		double probabilityBasedOnNegativeEffectsOfMutation = MAXIMUM_SURVIVABILITY
				/ Math.exp(SLOPE_OF_NEGATIVE_EFFECTS
						* Math.sqrt(sumOfSquareDifferences(arrayOfTraits,
								arrayOfLastGenerationsTraits)))
				+ MINIMUM_SURVIVABILITY;
		return probabilityBasedOnNegativeEffectsOfMutation;
	}

	private double evaluateSurvivalRateBasedOnDegreeOfAdaption() {
		Double[] arrayOfTraits = traits.getArrayOfVariables();
		Double[] arrayOfOptimalTraits = currentOptimalTraits
				.getArrayOfVariables();
		double probabilityOfSurvivalBasedOnAdaption = MAXIMUM_SURVIVABILITY
				/ Math.exp(SLOPE_OF_POSITIVE_EFFECTS
						* Math.sqrt(sumOfSquareDifferences(arrayOfTraits,
								arrayOfOptimalTraits))) + MINIMUM_SURVIVABILITY;
		return probabilityOfSurvivalBasedOnAdaption;
	}

	public boolean isAlive() {
		return alive;
	}

	public Phenotype reportTraits() {
		return traits.duplicate();
	}

	public void tryToReproduce() {
		if (Math.random() + probabilityOfSurvival < 1) {
			die();
		}
	}

	private void die() {
		alive = false;
	}

	private double sumOfSquareDifferences(Double[] doubles, Double[] doubles2) {
		double sumOfSquareDifferences = 0;
		for (int i = 0; i < doubles.length; i++) {
			double difference = doubles[i] - doubles2[i];
			double squareDifference = difference * difference;
			sumOfSquareDifferences = sumOfSquareDifferences + squareDifference;
		}
		return sumOfSquareDifferences;
	}
}
